/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.engine.executor.jasper;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.ScriptRuntimeException;
import cn.easyplatform.contexts.RecordContext;
import cn.easyplatform.contexts.ReportContext;
import cn.easyplatform.engine.executor.AbstractExecutor;
import cn.easyplatform.engine.runtime.jasper.ds.MemoryDataSource;
import cn.easyplatform.engine.runtime.jasper.ds.QueryDataSource;
import cn.easyplatform.entities.beans.report.JasperReportBean;
import cn.easyplatform.i18n.I18N;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.spi.listener.event.MessageEvent;
import cn.easyplatform.messages.vos.ReportVo;
import cn.easyplatform.messages.vos.executor.BeginVo;
import cn.easyplatform.messages.vos.executor.EndVo;
import cn.easyplatform.support.Closeable;
import cn.easyplatform.transaction.jdbc.JdbcTransactions;
import cn.easyplatform.util.RuntimeUtils;
import net.sf.jasperreports.engine.*;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class JasperReportExecutor extends AbstractExecutor<JasperReportBean> {

    private List<Closeable> closeableDs;

    @Override
    protected Object perform(Object... args) {
        ReportVo rv = (ReportVo) args[0];
        JasperReport jr = (JasperReport) task.getReportModel();
        if (!Strings.isBlank(task.getQuery())) {// 自定义报表
            closeableDs = new ArrayList<Closeable>();
            try {
                Map<String, Object> parameters = createParameters(
                        jr.getParameters(), rv);
                if (!Strings.isBlank(rv.getInit())) {
                    RecordContext rc = cc.getWorkflowContext().getRecord();
                    String code = RuntimeUtils.eval(cc, rv.getInit(), rc);
                    if (!code.equals("0000"))
                        throw new ScriptRuntimeException(code, cc.getMessage(
                                code, rc));
                    Closeable c = new MemoryDataSource(cc, rc, cc
                            .getWorkflowContext().getReport(rv.getId())
                            .getData(), task.getOnRecord());
                    parameters.put(rv.getId(), c);
                    closeableDs.add(c);
                }
                JasperPrint jp = JasperFillManager.fillReport(jr, parameters);
                ReportContext report = cc.getWorkflowContext().getReport(rv.getId());
                report.setPrint(jp);
                cc.send(new MessageEvent(task.getId(), new EndVo(jp)));
                return jp;
            } catch (JRException ex) {
                throw new EasyPlatformWithLabelKeyException(
                        "report.fill.error", ex, task.getId(), ex.getMessage());
            } finally {
                for (Closeable c : closeableDs)
                    c.close();
            }
        } else {// 报表
            Map<String, Object> parameters = createParameters(
                    jr.getParameters(), rv);
            try {
                cc.send(new MessageEvent(task.getId(), new BeginVo(I18N
                        .getLabel("report.execute.begin", task.getName()))));
                JasperPrint jp = JasperFillManager.fillReport(jr, parameters);
                ReportContext report = cc.getWorkflowContext().getReport(rv.getId());
                report.setPrint(jp);
                cc.send(new MessageEvent(task.getId(), new EndVo(jp)));
                return jp;
            } catch (JRException ex) {
                throw new EasyPlatformWithLabelKeyException(
                        "report.fill.error", ex, task.getId(), ex.getMessage());
            }
        }
    }

    private Map<String, Object> createParameters(JRParameter[] parameters,
                                                 ReportVo rv) {
        HashMap<String, Object> params = new HashMap<String, Object>();
        for (JRParameter param : parameters) {
            if (param.getValueClass() == JRDataSource.class) {
                if (closeableDs != null
                        && param.getName().equals("REPORT_DATA_SOURCE")) {
                    Closeable ds = new QueryDataSource(cc, task, false);
                    closeableDs.add(ds);
                    params.put(param.getName(), ds);
                }
            } else if (param.getValueClass() == JasperReport.class
                    && !param.getName().equals("JASPER_REPORT")) {
                JasperReportBean e = cc.getEntity(param.getName());
                if (e == null)
                    throw new EasyPlatformWithLabelKeyException(
                            "report.child.not.found", param.getName());
                if (e.getReportModel() == null)
                    throw new EasyPlatformWithLabelKeyException(
                            "report.entity.no.compile", param.getName());
                JasperReport jr = (JasperReport) e.getReportModel();
                params.put(param.getName(), jr);
                if (closeableDs != null && !Strings.isBlank(e.getQuery())) {// 如果是自定义报表
                    Closeable ds = new QueryDataSource(cc, e, true);
                    closeableDs.add(ds);
                    params.put(param.getName() + ".ds", ds);
                }
            } else if (!param.isSystemDefined())
                params.put(param.getName(), cc.getWorkflowContext().getRecord()
                        .getValue(param.getName()));
        }
        try {
            DataSource ds = cc.getProjectService().getDataSource(task.getDsId());
            params.put("REPORT_CONNECTION",
                    JdbcTransactions.getConnection(ds));
        } catch (SQLException ex) {
            throw new EasyPlatformWithLabelKeyException(
                    "report.connection.error", ex, task.getId());
        }
        return params;
    }

    @Override
    protected String getId() {
        return task.getId();
    }

}
